use std::io::{Read, Result, Write};

pub struct ReadStats<R>
    where R: Read
{
    reader: R,
    bytes_read: usize,
    read_count: usize,
}

impl<R: Read> ReadStats<R> {
    pub fn new(wrapped: R) -> ReadStats<R> {
        ReadStats {
            reader: wrapped,
            bytes_read: 0,
            read_count: 0,
        }
    }

    pub fn get_ref(&self) -> &R {
        &self.reader
    }

    pub fn bytes_through(&self) -> usize {
        self.bytes_read
    }

    pub fn reads(&self) -> usize {
        self.read_count
    }
}

impl<R: Read> Read for ReadStats<R> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.read_count += 1;
        match self.reader.read(buf) {
            Ok(n) => {
                self.bytes_read += n;
                Ok(n)
            },
            Err(e) => Err(e),
        }
    }
}

pub struct WriteStats<W>
    where W: Write
{
    writer: W,
    bytes_write: usize,
    write_count: usize,
}

impl<W: Write> WriteStats<W> {
    pub fn new(wrapped: W) -> WriteStats<W> {
        WriteStats {
            writer: wrapped,
            bytes_write: 0,
            write_count: 0,
        }
    }

    pub fn get_ref(&self) -> &W {
        &self.writer
    }

    pub fn bytes_through(&self) -> usize {
        self.bytes_write
    }

    pub fn writes(&self) -> usize {
        self.write_count
    }
}

impl<W: Write> Write for WriteStats<W> {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        self.write_count += 1;
        match self.writer.write(buf) {
            Ok(n) => {
                self.bytes_write += n;
                Ok(n)
            },
            Err(e) => Err(e),
        }
    }

    fn flush(&mut self) -> Result<()> {
        self.writer.flush()
    }
}